package com.kamistoat.meimeimall.meimeimallware.dubbo;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kamistoat.meimeimall.common.To.wareTo.HasStockMapTo;
import com.kamistoat.meimeimall.common.To.wareTo.LockOrderItemWareTo;
import com.kamistoat.meimeimall.common.To.wareTo.LockOrderWareTo;
import com.kamistoat.meimeimall.common.annotation.DubboAndHTTPExceptionCatch;
import com.kamistoat.meimeimall.common.dubboInterface.WareDubboInterface;
import com.kamistoat.meimeimall.common.exception.BizCodeEnum;
import com.kamistoat.meimeimall.common.exception.RRException;
import com.kamistoat.meimeimall.common.utils.R;
import com.kamistoat.meimeimall.meimeimallware.dao.WareOrderTaskDao;
import com.kamistoat.meimeimall.meimeimallware.dao.WareOrderTaskDetailDao;
import com.kamistoat.meimeimall.meimeimallware.entity.WareOrderTaskDetailEntity;
import com.kamistoat.meimeimall.meimeimallware.entity.WareOrderTaskEntity;
import com.kamistoat.meimeimall.meimeimallware.entity.WareSkuEntity;
import com.kamistoat.meimeimall.meimeimallware.enums.WareTaskStatusEnum;
import com.kamistoat.meimeimall.meimeimallware.service.WareOrderTaskDetailService;
import com.kamistoat.meimeimall.meimeimallware.service.WareOrderTaskService;
import com.kamistoat.meimeimall.meimeimallware.service.WareSkuService;
import com.kamistoat.meimeimall.meimeimallware.vo.WareOrderTaskAndDetailVo;
import io.seata.core.constants.DubboConstants;
import io.seata.rm.tcc.api.BusinessActionContext;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Dubbo接口实现
 */
@Service
@Component
public class WareDubboInterfaceImpl implements WareDubboInterface {
    @Autowired
    WareSkuService wareSkuService;
    @Autowired
    WareOrderTaskDao wareOrderTaskDao;
    @Autowired
    WareOrderTaskDetailDao wareOrderTaskDetailDao;
    @Autowired
    WareOrderTaskService wareOrderTaskService;
    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;
    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 查询商品列表是否有库存RPC
     *
     * @param skuIdList skuId列表
     * @return R对象，内含Map，key为商品SkuId，value为是否有货
     */
    @DubboAndHTTPExceptionCatch
    @Override
    public R hasStock(List<Long> skuIdList) {
        Map<Long, Boolean> skuStockMap = new HashMap<>();
        for (Long skuId : skuIdList) {
            // 注意，存在一个商品在多个仓库中都有库存的情况，因此要把多个仓库的库存合起来
            // 带缓查询
            List<WareSkuEntity> wareSkuList = wareSkuService.getWareSkuList(skuId);
            int stock = 0;
            if (wareSkuList != null && wareSkuList.size() > 0) {
                for (WareSkuEntity wareSkuEntity : wareSkuList) {
                    stock = stock + wareSkuEntity.getStock();
                    // 考虑到有人下订单但未支付的锁定库存情况，还要减去锁定的库存
                    stock = stock - wareSkuEntity.getStockLocked();
                }
            }
            skuStockMap.put(skuId, stock > 0);
        }
        HasStockMapTo hasStockMapTo = new HasStockMapTo();
        hasStockMapTo.setHasStockMap(skuStockMap);
        return R.ok().put("hasStockMapTo", JSON.toJSONString(hasStockMapTo));
    }

    /**
     * 创建订单时要锁定库存.是一个分布式事务分支
     *
     * @param lockOrderWareTo LockOrderWareTo 类
     * @return R对象
     */
    @DubboAndHTTPExceptionCatch
    @Override
    public R orderLockStock(BusinessActionContext businessActionContext, LockOrderWareTo lockOrderWareTo) {
        // 调用服务层方法执行库存锁定.
        // 服务层方法中如果某件商品库存锁定失败，会抛出异常，该异常会导致服务层本地的事务回滚.
        // 异常被这里的 try 捕获，并不继续抛出，而是以R对象结束RPC调用
        try {
            wareSkuService.orderLockStock(lockOrderWareTo);
            return R.ok();
        } catch (Exception exception) {
            if (exception instanceof RRException) {
                // 自定义异常，说明某一件商品的库存锁定失败
                return R.error(BizCodeEnum.NO_STOCK_EXCEPTION.getCode(),
                        BizCodeEnum.NO_STOCK_EXCEPTION.getMessage());
            } else {
                // 未知异常
                return R.error(exception.getMessage());
            }
        }
    }

    /**
     * 锁定库存事务的提交
     *
     * @param businessActionContext 事务上下文
     * @return 提交是否成功
     */
    @Override
    @DubboAndHTTPExceptionCatch
    public boolean orderLockStockCommit(BusinessActionContext businessActionContext) {
        LockOrderWareTo lockOrderWareTo = (LockOrderWareTo) businessActionContext.getActionContext("lockOrderWareTo");
        try {
            wareSkuService.orderLockStockCommit(lockOrderWareTo);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 锁定库存事务的回滚
     *
     * @param businessActionContext 事务上下文
     * @return 回滚是否成功
     */
    @Override
    @DubboAndHTTPExceptionCatch
    public boolean orderLockStockRollback(BusinessActionContext businessActionContext) {
        LockOrderWareTo lockOrderWareTo = (LockOrderWareTo) businessActionContext.getActionContext("lockOrderWareTo");
        try {
            wareSkuService.orderLockStockRollback(lockOrderWareTo);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 支付成功时，从锁定的库存值中扣除，真正扣除库存
     *
     * @param businessActionContext
     * @param orderSn               订单sn
     * @return R对象
     */
    @Override
    @DubboAndHTTPExceptionCatch
    public R handleSuccessPayWare(BusinessActionContext businessActionContext, String orderSn) {
        try {
            wareSkuService.handleSuccessPayWare(orderSn);
            return R.ok();
        } catch (Exception e) {
            e.printStackTrace();
            return R.error(e.getMessage());
        }
    }

    /**
     * 支付成功时，从锁定的库存值中扣除，真正扣除库存 提交动作。
     * 实际上不会做任何事情
     *
     * @param businessActionContext
     * @return
     */
    @Override
    @DubboAndHTTPExceptionCatch
    public boolean handleSuccessPayWareCommit(BusinessActionContext businessActionContext) {
        return true;
    }

    /**
     * 支付成功时，从锁定的库存值中扣除，真正扣除库存 回滚动作。
     * 检查库存工单状态，如果已经是成功扣除，则重新加回到锁定值上
     *
     * @param businessActionContext
     * @return
     */
    @Override
    @DubboAndHTTPExceptionCatch
    public boolean handleSuccessPayWareRollback(BusinessActionContext businessActionContext) {
        String orderSn = (String) businessActionContext.getActionContext("orderSn");
        try {
            wareSkuService.handleSuccessPayWareRollback(orderSn);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 下单时，以任务引擎的思路，先做任务的落库，只要任务落库成功，就视为下单阶段扣除库存成功。
     * 收到RPC请求，仅仅根据LockOrderWareTo制作订单-库存总工单，以及每件商品的单商品库存操作。
     * 不需要做分布式事务!!!! 只需要本地事务保证插入的原子性即可。
     *
     * @param lockOrderWareTo
     */
    @Override
    @Transactional
    @DubboAndHTTPExceptionCatch
    public R insertOrderWareOperation(LockOrderWareTo lockOrderWareTo) {
        WareOrderTaskEntity wareOrderInDatabase =
                wareOrderTaskDao.selectOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", lockOrderWareTo.getOrderSn()));
        // 幂等消费查询
        if(wareOrderInDatabase.getTaskStatus() != null) {
            // 重复消费直接返回
            return R.ok();
        }else {
            // 插入订单-库存总单
            WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
            wareOrderTaskEntity.setOrderSn(lockOrderWareTo.getOrderSn());
            wareOrderTaskEntity.setTaskStatus(WareTaskStatusEnum.WAITINGDEDUCTING.getCode());
            wareOrderTaskService.save(wareOrderTaskEntity);
            // 制作订单中单个商品的操作单
            List<WareOrderTaskDetailEntity> detailEntities = lockOrderWareTo.getLockList().stream().map(lockOrderItemWareTo -> {
                WareOrderTaskDetailEntity detailEntity = new WareOrderTaskDetailEntity();
                detailEntity.setTaskId(wareOrderTaskEntity.getId());
                detailEntity.setSkuId(lockOrderItemWareTo.getSkuId());
                detailEntity.setSkuNum(lockOrderItemWareTo.getCount());
                detailEntity.setSeckillSessionId(lockOrderItemWareTo.getSeckillSessionId());
                detailEntity.setLockStatus(WareTaskStatusEnum.WAITINGDEDUCTING.getCode());
                return detailEntity;
            }).collect(Collectors.toList());
            wareOrderTaskDetailService.saveBatch(detailEntities);
            WareOrderTaskAndDetailVo wareOrderTaskAndDetailVo = new WareOrderTaskAndDetailVo();
            wareOrderTaskAndDetailVo.setId(wareOrderTaskEntity.getId());
            wareOrderTaskAndDetailVo.setUserId(lockOrderWareTo.getUserId());
            wareOrderTaskAndDetailVo.setOrderSn(lockOrderWareTo.getOrderSn());
            wareOrderTaskAndDetailVo.setDetailEntityList(detailEntities);
            rabbitTemplate.convertAndSend("order-event-exchange", "order.create.result", wareOrderTaskAndDetailVo);
            return R.ok();
        }
    }
}
